package com.atguigu.aiproject.interceptor;

import com.atguigu.aiproject.constant.RedisKeyConstant;
import com.atguigu.aiproject.entity.User;
import com.atguigu.aiproject.utils.JwtUtils;
import com.atguigu.aiproject.utils.UserContext;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import reactor.util.annotation.NonNull;
import com.atguigu.aiproject.utils.HttpUtils;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

@Component
public class JwtInterceptor implements HandlerInterceptor {
    private final JwtUtils jwtUtils;
    private final RedisTemplate<String, Object> redisTemplate;
    private static final Logger log = LoggerFactory.getLogger(JwtInterceptor.class);
    private static final String USER_JWT_KEY = RedisKeyConstant.USER_JWT_KEY;// 用户id-->JWT
    private static final String JWT_USER_KEY = RedisKeyConstant.JWT_USER_KEY;// JWT对应的用户信息缓存

    public JwtInterceptor(JwtUtils jwtUtils, RedisTemplate<String, Object> redisTemplate) {
        this.jwtUtils = jwtUtils;
        this.redisTemplate = redisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws IOException {
        // 如果是OPTIONS预检请求，直接放行
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }

        System.out.println("请求源ip: " + HttpUtils.getClientIpAddress(request));

        // 从请求头中获取token
        String token = request.getHeader("Authorization");
        if (token == null || token.isEmpty()) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("look at your fuck invisible JWT");
            return false;
        }

        // 移除Bearer前缀
        if (token.startsWith("Bearer ")) {
            token = token.substring(7);
        }

        try {
            // 1. 尝试从Redis获取用户信息
            User user = getUserFromRedis(token);
            if (user != null) {//redis存在，验证单端登录
                // 单端登录验证：检查是否为最新token，如redis无法使用则单端登录失效
                if (!isLatestToken(token, user.getId())) {
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.getWriter().write("账号已在其他地方登录");
                    return false;
                }
                UserContext.setUsername(user.getLoginName());
                UserContext.setUserId(user.getId());
                return true;
            }

            // 2. Redis中没有，解析JWT
            Claims claims = jwtUtils.extractAllClaims(token);

            // 3. 从JWT构建用户信息
            user = buildUserFromClaims(claims);

            // 单端登录验证：检查是否为最新token，如redis无法使用则单端登录失效
            if (!isLatestToken(token, user.getId())) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.getWriter().write("账号已在其他地方登录");
                return false;
            }

            // 4. 尝试缓存到Redis（如果Redis可用）
            safeCacheUser(token, user, claims.getExpiration().getTime());

            // 5. 设置用户上下文
            UserContext.setUsername(user.getLoginName());
            UserContext.setUserId(user.getId());
            return true;
        } catch (ExpiredJwtException e) {
            // 删除可能存在的无效缓存
            safeDeleteFromRedis(token);
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("look at your fuck expired JWT");
            return false;
        } catch (Exception e) {
            // 统一处理其他异常（JWT解析错误、Redis异常等）
            safeDeleteFromRedis(token);
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("look at your fuck invalid jwt");
            return false;
        }
    }

    /**
     * 验证token是否有效（检查单端登录）
     */
    private boolean isLatestToken(String token, Integer userId) {
        try {
            String currentToken = (String) redisTemplate.opsForValue().get(USER_JWT_KEY + userId);
            if (currentToken == null) {
                return true;
            }
            return token.equals(currentToken);
        } catch (Exception e) {
            log.error("验证token失败", e);
            return true;
        }
    }

    /**
     * 从Redis获取用户信息
     */
    private User getUserFromRedis(String token) {
        try {
            String redisKey = JWT_USER_KEY + token;
            return (User) redisTemplate.opsForValue().get(redisKey);
        } catch (Exception e) {
            // Redis不可用时返回null，降级到JWT验证
            log.error("Redis挂了，降级到JWT验证：{}", e.getMessage());
            return null;
        }
    }

    /**
     * 安全地删除Redis中的token（Redis不可用时忽略）
     */
    private void safeDeleteFromRedis(String token) {
        try {
            redisTemplate.delete(JWT_USER_KEY + token);
        } catch (Exception e) {
            log.error("Redis挂了，忽略删除：{}", e.getMessage());
        }
    }

    /**
     * 安全地缓存用户信息到Redis（Redis不可用时忽略）
     */
    private void safeCacheUser(String token, User user, long expirationTime) {
        try {
            long expireTime = expirationTime - System.currentTimeMillis();
            if (expireTime > 0) {
                redisTemplate.opsForValue().set(JWT_USER_KEY + token, user, expireTime, TimeUnit.MILLISECONDS);
                redisTemplate.opsForValue().set(USER_JWT_KEY + user.getId(), token, expireTime, TimeUnit.MILLISECONDS);
            }
        } catch (Exception e) {
            log.error("Redis挂了，忽略缓存：{}", e.getMessage());
        }
    }

    /**
     * 从Claims构建用户对象
     */
    private User buildUserFromClaims(Claims claims) {
        String username = (String) claims.get("username");
        Integer userId = (Integer) claims.get("userId");
        User user = new User();
        user.setLoginName(username);
        user.setId(userId);
        return user;
    }

    @Override
    public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler, @Nullable Exception ex) {
        // 清除ThreadLocal中的数据，防止内存泄漏
        UserContext.clear();
    }
}
